home *** CD-ROM | disk | FTP | other *** search
- /* CleanMain.c */
- /*
- * Copyright © 1989, 1990 Martin Minow and MacTutor.
- *
- * You may use this software in any application and
- * redistribute this source without restriction as long
- * as this copyright and permission notice remains intact
- * and the source is not redistributed for profit and you
- * assume all responsibility for the proper operation of
- * this software.
- *
- * Written in Think C. Set Tabs every 2 characters.
- *
- * Operation:
- * CleanPICT can read a digitized image stored in a PICT
- * file (black and white at 300 dpi only) into a work
- * area. It then examines the picture for noise (islands
- * of pixels completely surrounded by the other color)
- * that are smaller than a selectable threshold. It can
- * then write the cleaned image to a file. Note that it
- * can eliminate islands of white pixels enclosed by the
- * black image, as well as islands of black pixels on
- * the white background.
- *
- * Limitations:
- * CleanPICT was developed to clean out one specific
- * image (a 300 dpi scanned image). It is extremely slow
- * (that PICT requires over an hour on a Mac SE).
- * CleanPICT keeps the entire PICT in memory, and
- * consequently needs a 1,200 K partition. It requires
- * a 4 Mb computer when run under Multifinder with the
- * Think C debugger.
- *
- * Homework assignment:
- * Generalize to arbitrary PICTs (72 dpi resolution, etc.)
- * Generalize to color.
- * Speed up.
- * Use a work file to minimize memory requirement.
- */
-
- #include "CleanPICT.h"
-
- /*
- * Menu organization
- */
- enum Menus {
- MENU_Apple = 1,
- MENU_File = 256,
- MENU_Edit = 257
- };
-
- enum Apple_Menu {
- Apple_About = 1
- };
-
- enum File_Menu {
- File_Open = 1,
- File_Clean,
- File_Set,
- File_Close,
- File_SaveAs,
- File_Debug,
- Unused,
- File_Quit
- };
-
- enum Edit_Menu {
- Edit_Undo,
- Edit_Unused,
- Edit_Cut,
- Edit_Copy,
- Edit_Paste,
- Edit_Clear
- };
-
- /*
- * Various stuff in the resource file.
- */
- enum {
- WIND_About = 1000,
- DLOG_Set_Threshold = 1000,
- ALRT_Advise = 2000,
- CURS_Spin = 2000,
- STRS_Info = 1
- };
-
- /*
- * Dialog and Alert item lists.
- */
- enum { /* DLOG_Set_Threshold */
- Thresh_OK = 1,
- Thresh_Cancel,
- Thresh_Text,
- Thresh_Value
- };
-
- enum { /* ALRT_Advise */
- Advise_Save = 1,
- Advise_Discard,
- Advise_Cancel
- };
-
- /*
- * isOurWindow is TRUE if its argument is our document.
- * isAboutWindow is TRUE if its argument is the progress
- * window.
- */
- #define isOurWindow(window) ( \
- (window) != NIL \
- && ((WindowPeek) (window))->windowKind == userKind \
- && (window) != aboutWindow \
- )
- #define isAboutWindow(window) ( \
- (window) != NIL && (window) == aboutWindow \
- )
-
- MenuHandle appleMenu;
- MenuHandle fileMenu;
- MenuHandle editMenu;
- WindowPtr aboutWindow;
- CursHandle spinCursor[2];
-
- void main(void);
- Boolean do_mouse(EventRecord);
- void do_command(long);
- void adjust_menus(WindowPtr);
- void adjust_edit_menu(Boolean);
- Boolean handle_events(void);
- void do_command(long);
- void setup(void);
- void do_about(void);
- void open_document(void);
- WindowPtr new_document(Str255);
- Boolean close_document(WindowPtr);
- void save_document(WindowPtr);
- void set_threshold(WindowPtr);
- void spin_cursor(void);
- void show_progress(WindowPtr);
- void display_statistics(WindowPtr, Boolean);
-
- /*
- * main()
- * Initialize the program and run the event loop.
- */
- void
- main()
- {
- EventRecord event;
- WindowPtr window;
- GrafPtr save_port;
- Rect box;
- long choice;
- Boolean quitNow; /* <CMD>. seen */
-
- setup();
- quitNow = FALSE;
- for (;;) {
- SystemTask();
- while (GetNextEvent(everyEvent, &event)
- && event.what != nullEvent) {
- if (event.what == activateEvt
- || event.what == updateEvt)
- window = (WindowPtr) event.message;
- else {
- window = FrontWindow();
- }
- switch (event.what) {
- case mouseDown:
- do_mouse(event);
- break;
- case activateEvt:
- if (isOurWindow(window) && aboutWindow != NIL)
- SetWRefCon(aboutWindow, window);
- break;
- case updateEvt:
- GetPort(&save_port);
- SetPort(window);
- BeginUpdate(window);
- if (!EmptyRgn((*window).visRgn)) {
- EraseRect(&window->portRect);
- if (isOurWindow(window)) {
- /*
- * Note that this must track the screen
- * display procedure in invert_seen_map().
- */
- CopyOSGrafPort(
- DOC.pictPort, window, window->visRgn);
- }
- else if (isAboutWindow(window))
- display_statistics(window, TRUE);
- }
- EndUpdate(window);
- SetPort(save_port);
- break;
- case keyDown:
- if ((event.message & charCodeMask) == '.'
- && (event.modifiers & cmdKey) != 0) {
- FlushEvents(keyDown | autoKey, 0);
- quitNow = TRUE;
- ARROW_CURSOR;
- }
- else if ((event.modifiers & cmdKey) != 0) {
- if (event.what == keyDown) {
- choice = MenuKey(
- event.message & charCodeMask);
- if (HiWord(choice) != 0)
- do_command(choice);
- else {
- SysBeep(10); /* Bogus <cmd> */
- }
- }
- }
- break;
- default:
- break;
- }
- }
- /*
- * We do the actual cleaning when the
- * machine is otherwise idle.
- */
- window = FrontWindow();
- if (isAboutWindow(window))
- window = (WindowPtr) GetWRefCon(window);
- if (isOurWindow(window)) {
- clean_picture(window, quitNow);
- if (DOC.state == Working) {
- if (aboutWindow != NIL)
- display_statistics(aboutWindow, FALSE);
- spin_cursor();
- show_progress(window);
- }
- else {
- quitNow = FALSE;
- }
- }
- }
- }
-
- /*
- * do_mouse(event)
- * Process a mouse button press, calling handlers as
- * needed.
- */
- static Boolean
- do_mouse(event)
- EventRecord event;
- {
- WindowPtr window;
- register int which_part;
- Rect box;
-
- which_part = FindWindow(event.where, &window);
- if (which_part == inMenuBar
- && isOurWindow(window) == FALSE)
- window = FrontWindow();
- adjust_menus(window);
- switch (which_part) {
- case inDesk:
- SysBeep(2);
- break;
- case inMenuBar:
- ARROW_CURSOR;
- do_command(MenuSelect(event.where));
- break;
- case inDrag:
- box = screenBits.bounds;
- box.top += GetMBarHeight();
- InsetRect(&box, 4, 4);
- DragWindow(window, event.where, &box);
- break;
- case inContent:
- if (FrontWindow() != window)
- SelectWindow(window);
- else {
- SetPort(window);
- }
- break;
- case inGoAway:
- if (isOurWindow(window)
- && TrackGoAway(window, event.where)) {
- close_document(window);
- if (aboutWindow != NIL)
- SetWRefCon(aboutWindow, NIL);
- }
- else if (isAboutWindow(window)
- && TrackGoAway(window, event.where)) {
- DisposeWindow(window);
- aboutWindow = NIL;
- }
- break;
- }
- return (FALSE);
- }
-
- /*
- * do_command()
- * Process a menu command.
- */
- void
- do_command(choice)
- long choice;
- {
- WindowPtr window;
- int item;
- long new;
- GrafPtr save_port;
- Str255 name;
-
- window = FrontWindow();
- item = LoWord(choice);
- switch (HiWord(choice)) {
- case MENU_Apple:
- GetItem(appleMenu, item, &name);
- if (item == Apple_About)
- make_about_window(window);
- else {
- adjust_edit_menu(TRUE);
- GetPort(&save_port);
- OpenDeskAcc(name);
- SetPort(save_port);
- adjust_edit_menu(FALSE);
- }
- break;
- case MENU_File:
- if (isAboutWindow(window))
- window = (WindowPtr) GetWRefCon(window);
- switch (item) {
- case File_Open: open_document(); break;
- case File_Clean:
- if (isOurWindow(window) && DOC.state == Idle)
- DOC.state = Init;
- break;
- case File_Close: close_document(window); break;
- case File_Set: set_threshold(window); break;
- case File_SaveAs:
- if (isOurWindow(window) == FALSE
- || DOC.state != Idle)
- SysBeep(10);
- else {
- save_document(window);
- }
- break;
- case File_Debug: Debugger(); break;
- case File_Quit:
- /*
- * A motion to adjourn is always in order.
- */
- while (isOurWindow(window)) {
- if (close_document(window) == FALSE)
- goto no_exit;
- window = FrontWindow();
- }
- ExitToShell();
- no_exit: break;
- }
- default:
- break;
- }
- HiliteMenu(0);
- }
-
- void
- make_about_window(window)
- WindowPtr window;
- {
- if (aboutWindow == NIL) {
- aboutWindow =
- GetNewWindow(WIND_About, NIL, -1L);
- if (aboutWindow != NIL)
- SetWRefCon(aboutWindow, window);
- }
- if (aboutWindow != NIL)
- SelectWindow(aboutWindow);
- }
-
- /*
- * adjust_menus()
- * Enable and disable menu items as needed.
- */
- static void
- adjust_menus(window)
- WindowPtr window;
- {
- if (isAboutWindow(window))
- window = (WindowPtr) GetWRefCon(window);
- if (isOurWindow(window)) {
- EnableItem(fileMenu, File_Clean);
- EnableItem(fileMenu, File_Set);
- EnableItem(fileMenu, File_Close);
- if (DOC.state == Idle)
- EnableItem(fileMenu, File_SaveAs);
- else {
- DisableItem(fileMenu, File_SaveAs);
- }
- CheckItem(
- fileMenu,
- File_Clean,
- DOC.state == Working
- );
- }
- else {
- DisableItem(fileMenu, File_Clean);
- DisableItem(fileMenu, File_Set);
- DisableItem(fileMenu, File_Close);
- DisableItem(fileMenu, File_SaveAs);
- }
- adjust_edit_menu(FALSE);
- }
-
- /*
- * adjust_edit_menu()
- * Fiddle the Edit menu. Mostly, enable
- * everything when switching to a desk accessory.
- * Note that CleanPict doesn't have anything
- * "editable" -- it would be reasonable to allow
- * moving the progress info to the Clipboard, though.
- */
- void
- adjust_edit_menu(enable)
- Boolean enable;
- {
- if (enable) {
- EnableItem(editMenu, Edit_Undo);
- EnableItem(editMenu, Edit_Cut);
- EnableItem(editMenu, Edit_Copy);
- EnableItem(editMenu, Edit_Paste);
- EnableItem(editMenu, Edit_Clear);
- }
- else {
- DisableItem(editMenu, Edit_Undo);
- DisableItem(editMenu, Edit_Cut);
- DisableItem(editMenu, Edit_Copy);
- DisableItem(editMenu, Edit_Paste);
- DisableItem(editMenu, Edit_Clear);
- }
- }
-
- /*
- * open_document()
- * Ask for a file (only allow PICT files). Read the
- * picture into a new window/document.
- */
- void
- open_document()
- {
- WindowPtr window;
- SFReply reply;
- int file;
- OSErr status;
- static Point where = { 85, 85 };
- SFTypeList typeList = { 'PICT' };
-
- SFGetFile(
- where, /* Where on the screen */
- NIL, /* Unused */
- NIL, /* no file filter */
- 1, /* Allow one file type */
- typeList, /* according to the list */
- NIL, /* no dialog hook */
- &reply /* reply goes here */
- );
- if (reply.good) {
- WATCH_CURSOR;
- SetVol(NIL, reply.vRefNum);
- status = FSOpen(reply.fName, reply.vRefNum, &file);
- if (status != noErr)
- SysBeep(10);
- else {
- window = new_document(reply.fName);
- if (window == NIL)
- SysBeep(10); /* No memory */
- else {
- status = read_picture(window, file);
- if (status != noErr) {
- DebugStr("\pread_picture failed");
- }
- }
- FSClose(file);
- }
- ARROW_CURSOR;
- }
- }
-
- /*
- * close_document()
- * Close the document in the current (front) window.
- * Dump picture. Return TRUE on success, FALSE if
- * the user cancels.
- */
- Boolean
- close_document(window)
- WindowPtr window;
- {
- short size;
- Cell cell;
-
- if (isOurWindow(window) == FALSE)
- return (TRUE);
- if (DOC.dirty) {
- ParamText(DOC.fileName, NIL, NIL, NIL);
- switch (CautionAlert(ALRT_Advise, NIL)) {
- case Advise_Save:
- save_document(window);
- break;
- case Advise_Discard:
- break;
- case Advise_Cancel:
- return (FALSE);
- }
- }
- WATCH_CURSOR;
- DeleteOSGrafPort(DOC.pictPort);
- CloseWindow(window);
- DisposPtr(window);
- window = NIL;
- CheckItem(fileMenu, File_Clean, FALSE);
- ARROW_CURSOR;
- return (TRUE);
- }
-
- /*
- * new_document()
- * Build a document: get memory for the WindowRecord and
- * our attached information. Offset the window with
- * respect to other windows and make the window. If
- * this succeeds, read the picture.
- */
- WindowPtr
- new_document(title)
- Str255 title;
- {
- WindowPtr window;
- DocumentPtr doc;
- Rect box;
- static long sequence; /* Identify windows */
-
- doc = (DocumentPtr) NewPtrClear(sizeof (DocumentRecord));
- if (doc == NIL)
- return (NIL);
- /*
- * Start with a tiny window: the picture reader
- * sets the correct size.
- */
- SetRect(&box, 0, GetMBarHeight() * 2, 32, 0);
- box.bottom = box.top + 32;
- window = NewWindow(
- doc, /* Allocated storage */
- &box, /* Display Rect */
- title, /* Title */
- FALSE, /* Invisible on creation */
- noGrowDocProc, /* Window type */
- -1L, /* Show in front */
- TRUE, /* GoAway box */
- ++sequence /* RefCon (debug only) */
- );
- if (window == NIL) {
- DisposPtr(doc);
- return (NIL);
- }
- pstrcpy(DOC.fileName, title);
- DOC.threshold = THRESHOLD;
- SetPort(window);
- return (window);
- }
-
- /*
- * save_document()
- * Write the current picture as a PICT. The creator
- * is hard-wired for Canvas.
- */
- void
- save_document(window)
- WindowPtr window;
- {
- SFReply reply;
- int file;
- OSErr status;
- static Point where = { 85, 85 };
- SFTypeList typeList = { 'PICT' };
- Str255 default_filename;
-
- if (isOurWindow(window) == FALSE)
- return;
- pstrcpy(default_filename, "\pNew ");
- pstrcat(default_filename, DOC.fileName);
- SFPutFile(
- where,
- "\pSave PICT as",
- default_filename,
- NIL,
- &reply
- );
- if (reply.good == FALSE)
- goto exit;
- else {
- WATCH_CURSOR;
- (void) FSDelete(reply.fName, reply.vRefNum);
- status = Create(
- reply.fName, /* File name */
- reply.vRefNum, /* Volume reference */
- 'DAD2', /* Creator == Canvas */
- 'PICT' /* File type */
- );
- if (status != noErr) {
- DebugStr("\pCan't create file");
- goto exit;
- }
- status = FSOpen(reply.fName, reply.vRefNum, &file);
- if (status != noErr) {
- DebugStr("\pCan't open newly created file");
- goto exit;
- }
- write_picture(window, file);
- FSClose(file); /* Should check for */
- FlushVol(NIL, reply.vRefNum); /* Errors here. */
- DOC.dirty = FALSE;
- exit:
- ARROW_CURSOR;
- }
- }
-
- /*
- * setup()
- * One-time initialization.
- */
- void
- setup()
- {
- InitGraf(&thePort);
- InitFonts();
- FlushEvents(everyEvent, 0);
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(NIL);
- InitCursor();
- WATCH_CURSOR;
- MaxApplZone();
- appleMenu = GetMenu(MENU_Apple);
- fileMenu = GetMenu(MENU_File);
- editMenu = GetMenu(MENU_Edit);
- spinCursor[0] =
- (CursHandle) GetResource('CURS', CURS_Spin);
- spinCursor[1] =
- (CursHandle) GetResource('CURS', CURS_Spin + 1);
- AddResMenu(appleMenu, 'DRVR');
- InsertMenu(appleMenu, 0);
- InsertMenu(fileMenu, 0);
- InsertMenu(editMenu, 0);
- DrawMenuBar();
- ARROW_CURSOR;
- }
-
- /*
- * pstrcat()
- * Concatenate a pascal string to another.
- */
- void
- pstrcat(out, in)
- void *out;
- void *in;
- {
- long start;
- long length;
-
- start = ((unsigned char *) out)[0];
- length = ((unsigned char *) in)[0];
- if ((start + length) > 255)
- length = 255 - start;
- ((unsigned char *) out)[0] = start + length;
- BlockMove(
- ((unsigned char *) in) + 1,
- ((unsigned char *) out) + start + 1,
- length
- );
- }
-
- /*
- * set_threshold()
- * Ask the user for a threshold value for the current
- * window.
- */
- void
- set_threshold(window)
- WindowPtr window;
- {
- Str255 work;
- long newSize;
- GrafPtr old_port;
- DialogPtr dialog;
- int type;
- Handle handle;
- Rect box;
- int item;
-
- if (isOurWindow(window) == FALSE)
- return;
- GetPort(&old_port);
- dialog = GetNewDialog(DLOG_Set_Threshold, NIL, -1L);
- ShowWindow(dialog);
- SetPort(dialog);
- again:
- newSize = DOC.threshold;
- NumToString(newSize, work);
- GetDItem(dialog, Thresh_Value, &type, &handle, &box);
- SetIText(handle, work);
- SelIText(dialog, Thresh_Value, 0, 32767);
- ModalDialog(NIL, &item);
- switch (item) {
- case Cancel:
- break;
- case OK:
- GetDItem(dialog, Thresh_Value, &type, &handle, &box);
- GetIText(handle, work);
- StringToNum(work, &newSize);
- if (newSize <= 0 || newSize >= (XMAX * YMAX)) {
- SysBeep(10);
- goto again;
- }
- DOC.threshold = newSize;
- break;
- default: /* Can't happen */
- break;
- }
- DisposDialog(dialog);
- SetPort(old_port);
- }
-
- /*
- * spin_cursor()
- * Blink the cursor: called during idle time.
- */
- void
- spin_cursor()
- {
- static int index = 0;
- static Ulong last = 0;
- long now;
-
- GetDateTime(&now);
- if ((now - last) > 0) {
- index = 1 - index;
- SetCursor(*spinCursor[index]);
- last = now;
- }
- }
-
- /*
- * show_progress()
- * Update the window title every twenty rows.
- * Note that we will be called several times during
- * any particular row. There is an interlock to
- * prevent the menu bar from flashing.
- */
- void
- show_progress(window)
- WindowPtr window;
- {
- Str255 title;
- Ulong theRow, botRow;
- Boolean doneTwenty;
- static Boolean needUpdate = FALSE;
-
- doneTwenty = (DOC.center.v % 20) == 0;
- if (needUpdate == doneTwenty) {
- if (needUpdate == FALSE) /* Just leave 20 row? */
- needUpdate = TRUE; /* Get ready for update */
- else { /* Just entered 20 row */
- theRow = DOC.center.v; /* Want row and bottom */
- botRow = DOC.bottom; /* as long int's */
- NumToString((theRow * 100) / botRow, title);
- pstrcat(title, "\p% done");
- SetWTitle(window, title);
- needUpdate = FALSE; /* Skip until next 20 */
- }
- }
- }
-
- /*
- * display_statistics shows the result of the examination
- * process.
- */
- #define SHOWLINE do { \
- TextBox( \
- &theLine[1], theLine[0], &textRect, teJustLeft); \
- OffsetRect(&textRect, 0, lineSize); \
- theLine[0] = 0; \
- } while (0)
-
- #define LINETEXT(t) do { \
- pstrcpy(theLine, "\p "); \
- pstrcat(theLine, t); \
- } while (0)
- #define DRAWVALUE(v) do { \
- NumToString((v), work); \
- if (work[0] == 0) \
- pstrcat(theLine, "\p0"); \
- else { \
- pstrcat(theLine, work); \
- } \
- } while (0)
- #define DRAWTIME(v) do { \
- pstrcat(theLine, "\p:"); \
- leading_zero(theLine, (v), 2); \
- } while (0)
- #define LINEVALUE(before, value, after) do { \
- LINETEXT(before); \
- DRAWVALUE(value); \
- pstrcat(theLine, "\p "); \
- pstrcat(theLine, after); \
- SHOWLINE; \
- } while (0)
- #define RATIO(v1, v2, after) do { \
- compute_fraction(work, (v1), (v2)); \
- LINETEXT(work); \
- pstrcat(theLine, "\p "); \
- pstrcat(theLine, after); \
- SHOWLINE; \
- } while (0);
-
- void compute_fraction(Str255, Ulong, Ulong);
- void leading_zero(Str255, Ulong, int);
-
- /*
- * Display the "About" (and/or statistics) window.
- * This is excessively crude.
- */
- void
- display_statistics(about_window, always)
- WindowPtr about_window;
- Boolean always;
- {
- WindowPtr window;
- GrafPtr oldPort;
- FontInfo info;
- int i;
- Boolean more;
- Str255 work, theLine;
- int lineSize, lineHeight;
- Rect textRect;
- Ulong now, total, temp;
-
- GetPort(&oldPort);
- SetPort(about_window);
- TextFont(geneva);
- TextSize(9);
- GetFontInfo(&info);
- lineHeight = info.ascent + info.descent;
- lineSize = lineHeight + info.leading;
- textRect = thePort->portRect;
- textRect.bottom = textRect.top + lineHeight;
- i = 0;
- window = (WindowPtr) GetWRefCon(about_window);
- if (isOurWindow(window) == FALSE
- || DOC.start == 0) {
- do {
- GetIndString(theLine, STRS_Info, ++i);
- more = (theLine[0] != 0);
- SHOWLINE;
- } while (more);
- }
- else {
- if (DOC.state == Working) {
- GetDateTime(&now);
- DOC.elapsed = now - DOC.start;
- }
- if (always
- || DOC.elapsed != DOC.elapsed_shown) {
- DOC.elapsed_shown = DOC.elapsed;
- do {
- GetIndString(theLine, STRS_Info, ++i);
- more = (theLine[0] != 0);
- SHOWLINE;
- } while (more);
- total = ((Ulong) width(DOC.pictPort->portRect))
- * DOC.center.v;
- LINEVALUE("\pTotal ", total, "\ppixels.");
- LINEVALUE("\pExamined ", DOC.examined, "\ppixels.");
- LINEVALUE(
- "\pDeleted ", DOC.found, "\pnoise islands.");
- LINEVALUE(
- "\pElapsed time ", DOC.elapsed, "\pseconds.");
- LINETEXT("\pElapsed time ");
- DRAWVALUE(DOC.elapsed / 3600L);
- DRAWTIME((DOC.elapsed / 60L) % 60L);
- DRAWTIME(DOC.elapsed % 60L);
- SHOWLINE;
- RATIO(total, DOC.elapsed, "\ptotal pixels/sec.");
- RATIO(
- DOC.examined, DOC.elapsed,
- "\pexamined pixels/sec."
- );
- RATIO(
- DOC.found, DOC.examined, "\pfound / examined.");
- }
- }
- SetPort(oldPort);
- }
-
- /*
- * compute_fraction()
- * Convert the result (a ratio) to a printable string.
- */
- void
- compute_fraction(result, numer, denom)
- Str255 result;
- Ulong numer;
- Ulong denom;
- {
- if (denom == 0)
- pstrcpy(result, "\p<0>");
- else {
- NumToString(numer / denom, result);
- pstrcat(result, "\p.");
- leading_zero(
- result, ((numer * 1000L) / denom) % 1000, 3);
- }
- }
-
- /*
- * leading_zero()
- * This is a crude function to display a value with
- * leading zeros.
- */
- void
- leading_zero(result, value, field_width)
- Str255 result;
- Ulong value;
- int field_width;
- {
- Str255 work;
- register int i;
-
- NumToString(value, work);
- for (i = work[0]; i < field_width; i++)
- pstrcat(result, "\p0");
- pstrcat(result, work);
- }
-